/*
 * Copyright 2021 Shaoguang.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef TINYSCREENRECORDER_H
#define TINYSCREENRECORDER_H

#include <QObject>
#include <QApplication>
#include <QDesktopWidget>

class TinyScreenRecorderPrivate;

/**
 * \class TinyScreenRecorder
 * \brief A tiny screen recorder based on Qt and avilib.
 *
 * TinyScreenRecorder recording the whole desktop or specified widget, it's tiny and easy.
 * Limitations : only avi format video (in 'MJPG' encoding, no audio) is supported and is inefficiency.
 *
 * qmake: QT += core gui widgets
 *
 * \note QT version is required to be greater than or equal to 5.6
 */
class TinyScreenRecorder : public QObject
{
    Q_OBJECT
    Q_DECLARE_PRIVATE(TinyScreenRecorder)
public:
    explicit TinyScreenRecorder(QObject *parent = nullptr);
    ~TinyScreenRecorder();

    /**
     * Set the target widget and the region of interest to recording.
     *
     * \sa targetWidget(), roiRect()
     */
    void setRoiArguments(QWidget* targetWidget = QApplication::desktop(), QRect roiRect = QRect());
    /**
     * Returns the widget of recording target.
     * Default is full desktop (QApplication::desktop()).
     *
     * \sa setRoiArguments()
     */
    QWidget* targetWidget() const;
    /**
     * Returns the region of interest of recording target widget.
     *
     * see QPixmap::save()
     * \sa setRoiArguments(), targetWidget()
     */
    QRect roiRect() const;

public Q_SLOTS:
    /**
     * Starts the recording. The recording process will run in the sub thread, so it won't block.
     * If the recording process is already running, do nothing.
     * If start successfully, the signal recordingStarted will be emitted; otherwise, the signal errorOccurred may be emitted.
     *
     * \sa stop(), recordingStarted(), errorOccurred()
     */
    void start();

    /**
     * Stop recording, the signal recordingFinished() will be emitted after finished.
     *\sa start(), waitForFinished(), recordingFinished()
     */
    void stop();

public:
    /**
     * Blocks until the recording has finished and the isFinished() is true, or until \a msecs milliseconds have passed.
     * Returns true if the recording finished; otherwise returns false (if the operation timed out).
     *
     * \sa recordingFinished(), recordingStarted(), errorOccurred()
     */
    bool waitForFinished(int msec = 30000);

    /**
     * Returns true if the recording is finished; otherwise returns false.
     *
     * \sa isRunning()
     */
    bool isFinished() const;

    /**
     * Returns true if the recording is running; otherwise returns false.
     *
     * \sa isFinished()
     */
    bool isRunning() const;

    /**
     * Returns the output video file name.
     * Only supports recording video in avi format for now, so the file suffix should be set to "avi".
     *
     * \sa setVideoFileName()
     */
    const QString& videoFileName() const;
    /**
     * Set the output video file name.
     *
     * \sa videoFileName()
     */
    void setVideoFileName(const QString& fileName);

    /**
     * The \a quality factor must be in the range [0,100] or -1.
     * Specify 0 to obtain small compressed files, 100 for large uncompressed files, and -1 to use the default settings.
     *
     * see QPixmap::save()
     * \sa videoQuality()
     */
    void setVideoQuality(int quality = -1);
    /**
     * Returns the video quality.
     *
     * \sa setVideoQuality()
     */
    int videoQuality() const;

    /**
     * The \a fps must greater 0.
     * If auto fps is set, this function doesnot work.
     *
     * \sa videoFps(), setAutoFps()
     */
    void setVideoFps(double fps);
    /**
     * Returns the fps.
     * If auto fps is set, the automatically detected fps will be returned after call start().
     *
     * \sa setVideoFps()
     */
    double videoFps() const;

    /**
     * Set auto fps enable or not.
     *
     * \sa isAutoFps()
     */
    void setAutoFps(bool automatic);
    /**
     * If auto fps is set, returns true; otherwise returns false.
     * Default is true.
     *
     * \sa setAutoFps()
     */
    bool isAutoFps() const;

    /**
     * Set avi error printable. avilib error will print when avilib error occurred.
     *
     * \sa disablePrintAviError(), setAviErrorPrintable(), isAviErrorPrintable()
     */
    static void enablePrintAviError();
    /**
     * Set avi error nonprintable. avilib error will not print when avilib error occurred.
     *
     * \sa enablePrintAviError(), setAviErrorPrintable(), isAviErrorPrintable()
     */
    static void disablePrintAviError();
    /**
     * Set avi error printable to \a printable.
     * \sa isAviErrorPrintable()
     */
    static void setAviErrorPrintable(bool printable);
    /**
     * If avi error printable, returns true; otherwise returns false.
     * Default is true.
     *
     * \sa setAviErrorPrintable()
     */
    static bool isAviErrorPrintable();

Q_SIGNALS:
    /**
     * This signal is emitted when an error occurs in the screen recorder.
     * The specified \a error describes the detail of error that occurred.
     */
    void errorOccurred(const QString& error);
    /**
     * This signal is emitted when the recording has started.
     * \note Note: This is a private signal. It can be used in signal connections but cannot be emitted by the user.
     */
    void recordingStarted();
    /**
     * This signal is emitted when one frame is recorded sucessfully.
     * \arg frameCount total frames recorded successfully.
     * \note Note: This is a private signal. It can be used in signal connections but cannot be emitted by the user.
     */
    void frameRecorded(qint64 frameCount);
    /**
     * This signal is emitted when one frame is failed to record.
     * \arg frameCount total frames missed.
     * \note Note: This is a private signal. It can be used in signal connections but cannot be emitted by the user.
     */
    void frameMissed(qint64 frameCount);
    /**
     * This signal is emitted when the recording has fnished.
     * \note Note: This is a private signal. It can be used in signal connections but cannot be emitted by the user.
     */
    void recordingFinished();
    /**
     * This signal is emitted when auto fps is set and the fps detecting has fnished.
     * \note Note: This is a private signal. It can be used in signal connections but cannot be emitted by the user.
     */
    void fpsDetected(double fps);

private:
    Q_DISABLE_COPY(TinyScreenRecorder)
    TinyScreenRecorderPrivate* d_ptr;
};

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug, const TinyScreenRecorder*);
QDebug operator<<(QDebug, const TinyScreenRecorder&);
#endif

#endif // TINYSCREENRECORDER_H
